Skip to content

Add orphaned-props category to the Code Health check#2022

Draft
GregorShear wants to merge 2 commits into
mainfrom
claude/orphaned-react-props-check
Draft

Add orphaned-props category to the Code Health check#2022
GregorShear wants to merge 2 commits into
mainfrom
claude/orphaned-react-props-check

Conversation

@GregorShear

Copy link
Copy Markdown
Contributor

Summary

Adds a new orphaned-props category to the existing Code Health PR check. A prop can be declared (and even read internally) on a component while no caller anywhere in src/ ever passes it, so it's always its default/undefined value in practice — Knip doesn't catch this since it only tracks module-level exports, not JSX call sites.

orphaned-props.mjs resolves each component's Props type syntactically (inline object types, named interfaces/type aliases, extends, intersections, and Pick/Omit/Partial/Required/Readonly) and cross-references it against every JSX invocation in the tree, matching usage sites back to declarations via this repo's absolute src/-rooted imports — no full type-checker needed.

A component is skipped (never reported) rather than guessed at when:

  • its Props type doesn't resolve cleanly (generics, mapped/conditional types, a type re-exported through a barrel, etc.)
  • any call site spreads attributes (<Foo {...rest} />) — the spread could supply any prop
  • it has zero call sites — that's dead-code territory Knip already covers

children is treated as satisfied by non-empty JSX content, not just an explicit attribute.

Results merge into the existing Knip JSON report as a new orphanedProps category, so knip-delta.mjs's base-vs-head ratchet and PR-comment rendering pick it up for free — no changes to the diffing/commenting logic itself beyond registering the category's label and explainer text.

Heads up

Run against the current tree, this finds ~98 pre-existing orphaned props. Nothing here is blocking (the whole pipeline is informational/ratchet-forward — only new issues a PR introduces show as +), but the first comment on this PR will show that as a sizeable "carried" baseline.

Test plan

  • Ran the script directly against the full src/ tree; spot-checked several findings by hand against the source (ConnectionStatusBadge.compact, CardWrapper.sx, MessageWithEmphasis.emphasisContent, OnboardGuard.children) — all genuine, no false positives found in the sample
  • Fixed a double-counting bug where components declared as function Foo() {} export default Foo; were registered under two keys pointing at the same entry
  • Verified knip-delta.mjs renders the new category correctly with a synthetic base/head diff
  • Let the Code Health workflow run for real on this PR and review the actual comment

A prop can be declared (and even read internally) on a component while no
caller anywhere in src/ ever passes it, so it's always its default/undefined
value in practice. Knip doesn't catch this — it only tracks module-level
exports, not JSX call sites.

orphaned-props.mjs resolves each component's Props type syntactically (inline
literals, named interfaces/type aliases, extends, intersections, and
Pick/Omit/Partial/Required/Readonly) and cross-references it against every JSX
invocation in the tree, using this repo's absolute src/-rooted imports to match
usage sites back to declarations without needing a full type-checker. A
component is skipped rather than guessed at when its Props type doesn't
resolve cleanly, when any call site spreads attributes (which could supply any
prop), or when it has zero call sites (that's dead-code territory, already
covered by Knip). children is treated as satisfied by non-empty JSX content,
not just an explicit attribute.

Results merge into the existing Knip JSON report as a new orphanedProps
category, so knip-delta.mjs's existing base-vs-head ratchet and PR-comment
rendering pick it up for free.
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

⚪ Code Health

No change to the dead-code surface.

60 Dead files

File imported nowhere — delete (or import) it.

     src/hooks/useDelay.ts
     src/hooks/useDraft.ts
     src/hooks/useExpressWorkflowAuth.ts
     src/pages/NoGrants.tsx
     src/pages/OAuth.tsx
     src/services/encryption.ts
     src/types/global.ts
     src/types/vitest.ts
     src/components/graphs/TaskHoursByMonthGraph.tsx
     src/components/graphics/PoweredByEstuaryWatermark.tsx
…and 50 more

82 Dead symbols

Symbol used nowhere — export hides it from the linter; delete it

     src/context/Theme.tsx : logoColors
     src/context/Theme.tsx : intensifiedOutlineThick
     src/context/Theme.tsx : tableAlternateRowsSx
     src/context/Theme.tsx : draggableChipIconSx
     src/context/Theme.tsx : hiddenButAccessibleInput
     src/context/Theme.tsx : primaryColoredBackground_hovered
     src/context/Theme.tsx : detailsPanelBgColor
     src/context/Theme.tsx : menuBackgroundColor
     src/context/Theme.tsx : flexGrowToSiblingsSx
     src/context/Theme.tsx : shardTableRow
…and 72 more

14 Dead enum members

An enum member referenced nowhere

     src/services/supabase.ts : CONNECTOR_TAGS
     src/services/supabase.ts : DRAFTS_EXT
     src/services/supabase.ts : TASKS_BY_DAY
     src/stores/names.ts : QUEUED_TASKS
     src/stores/Tables/hooks.ts : accessGrants
     src/stores/Tables/hooks.ts : accessLinks
     src/stores/Tables/hooks.ts : billing
     src/stores/Tables/hooks.ts : connectors
     src/stores/Tables/hooks.ts : entitySelector
     src/stores/Tables/hooks.ts : prefixes
…and 4 more

178 Unnecessary exports

Exported but only used within its own file — just drop export

     src/context/Theme.tsx : sample_blue
     src/context/Theme.tsx : successDark
     src/context/Theme.tsx : infoMain
     src/context/Theme.tsx : chipDraggableIndex
     src/context/Theme.tsx : headerLinkIndex
     src/context/Theme.tsx : alternativeDataGridHeader
     src/context/Theme.tsx : connectorCardLogoBackground
     src/context/Theme.tsx : wrappingTableCell
     src/utils/connector-utils.ts : DEKAF_IMAGE_PREFIX
     src/utils/dataPlane-utils.ts : dataPlaneFetcher_list
…and 168 more

8 Unused dependencies

In package.json but never imported

     package.json : @mui/lab
     package.json : @testing-library/jest-dom
     package.json : @urql/exchange-retry
     package.json : logrocket-react
     package.json : stripe
     package.json : @types/logrocket-react
     package.json : @types/react-inspector
     package.json : sharp

98 Orphaned props

Declared on the component but no caller ever passes it

     src/components/tables/RowActions/Shared/ConfirmationAlert.tsx : ConfirmationAlert.potentiallyDangerousUpdate
     src/components/tables/RowActions/Shared/ConfirmationAlert.tsx : ConfirmationAlertWrapper.potentiallyDangerousUpdate
     src/components/shared/pickers/TimePickerCTA.tsx : TimePickerCTA.removeOffset
     src/components/shared/Entity/ValidationErrorSummary/SectionError.tsx : SectionErrorWrapper.errorMessage
     src/components/editor/DraftSpec.tsx : DraftSpecEditor.localZustandScope
     src/components/editor/DraftSpec.tsx : DraftSpecEditor.editorHeight
     src/components/editor/DraftSpec.tsx : DraftSpecEditor.entityName
     src/components/transformation/create/Schema/Editor.tsx : DerivationSchemaEditor.editorHeight
     src/components/editor/EditorWithFileSelector.tsx : EditorWithFileSelector.onChange
     src/components/editor/EditorWithFileSelector.tsx : EditorWithFileSelector.toolbarHeight
…and 88 more

@GregorShear GregorShear force-pushed the claude/code-health-checks branch 4 times, most recently from a4152a1 to 604416d Compare July 2, 2026 20:20
Base automatically changed from claude/code-health-checks to main July 2, 2026 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant